using System;
using System.Collections.Generic;

using System.Text;

using Mud.Common.Messages;
using Mud.Common.Physical;
using Mud.Server.Physical;
using Mud.Common;



namespace Mud.Server.Client.Commands
{

    /// <summary>
    /// Command to move the creature
    /// </summary>
    [MessageHandler(MessageTypeIds.CommandCreatureMove)]
    public class CommandCreatureMove : InteruptableCommand
    {

        private MessageCommandCreatureMove _command;
        private Creature _creature;

        private int _currentPathIndex;
        private List<Location> _path;


        public override ControlerCommandType Type
        {
            get { return ControlerCommandType.Iterative; }
        }

        public override IMessage CanExecute()
        {
            _command = AssertIsMessage<MessageCommandCreatureMove>(CommandData);
            MovementType movementType = _command.MovementType;
            switch (movementType)
            {
                case MovementType.Walking:
                    Duration = 800;
                    break;
                case MovementType.Running:
                    Duration = 400;
                    break;
            }
            _creature = CommandControler.ControledCreature;
            if (_creature.CurrentArea == null)
                return new MessageResponseError("You are not in any area.");

            return ResultWithReason.WrapMessage(_creature.CanMove(new Location(_command.X, _command.Y), movementType));
        }

        protected override IMessage StartCommandImpl()
        {
            CalculatePath();

            Location target = _path[0];

            MovementType type = _command.MovementType;

            IMessage canMove = ResultWithReason.WrapMessage(_creature.CanMove(target, type));
            if (MessageResponseOk.Instance != canMove)
            {
                EndCommand();
                return canMove;
            }

            switch (type)
            {
                case MovementType.Walking:
                    Duration = 800;
                    break;
                case MovementType.Running:
                    Duration = 400;
                    break;
            }

            Orientation direction = PhysicalUtil.CalculateDirection(_creature.Location, target);
            if (_creature.PhysicalProperties.Orientation != direction)
            {
                IMessage canTurn = ResultWithReason.WrapMessage(_creature.CanTurn(direction));
                if (MessageResponseOk.Instance != canTurn)
                    return canTurn;
                _creature.PhysicalProperties.Orientation = direction;
                EventCreatureTurned turned = new EventCreatureTurned(_creature, direction);
                turned.Send(_creature.CurrentArea);
            }

            EventCreatureMoveStarted e = new EventCreatureMoveStarted(_creature, target, type, Duration);
            e.Send(_creature.CurrentArea);
			Server.CurrentScheduler.ScheduleMethod(TimeCommandStarted + Duration, Iteration);

            return MessageResponseOk.Instance;
        }

        private void Iteration()
        {
            Location target = _path[_currentPathIndex];

            IMessage canMove = ResultWithReason.WrapMessage(_creature.CanMove(target, _command.MovementType));
            if (MessageResponseOk.Instance != canMove)
            {
                EndCommand();
                return;
            }

            _creature.Location = target;
            EventCreatureMoveEnded e = new EventCreatureMoveEnded(_creature, target);
            e.Send(_creature.CurrentArea);

            _currentPathIndex++;
            if (_currentPathIndex < _path.Count)
            {
                target = _path[_currentPathIndex];

                canMove = ResultWithReason.WrapMessage(_creature.CanMove(target, _command.MovementType));
                if (MessageResponseOk.Instance != canMove)
                {
                    EndCommand();
                    return;
                }


                Orientation direction = PhysicalUtil.CalculateDirection(_creature.Location, target);
                if (_creature.PhysicalProperties.Orientation != direction)
                {
                    IMessage canTurn = ResultWithReason.WrapMessage(_creature.CanTurn(direction));
                    if (MessageResponseOk.Instance != canTurn)
                    {
                        EndCommand();
                        return;
                    }
                    _creature.PhysicalProperties.Orientation = direction;
                    
                    EventCreatureTurned turned = new EventCreatureTurned(_creature, direction);
                    turned.Send(_creature.CurrentArea);
                }

                EventCreatureMoveStarted started = new EventCreatureMoveStarted(_creature, target, _command.MovementType, Duration);
                started.Send(_creature.CurrentArea);
				Server.CurrentScheduler.ScheduleMethod(Server.CurrentTimeIndex + Duration, Iteration);
            }
            else
            {
                EndCommand();
            }
        }

        protected override void InteruptImpl(ControlerCommand c)
        {
            Interupt(true);
        }

        protected override void InteruptImpl(PhysicalEvent e)
        {
            Interupt(false);
        }
       

        private void Interupt(bool byCommand)
        {
            // we go back to the same square that we started from
            Location target = _creature.Location;
            EventCreatureMoveEnded e = new EventCreatureMoveEnded(_creature, target);
            e.Send(_creature.CurrentArea);
			Server.CurrentScheduler.RemoveScheduledMethod(Iteration);
            this.Cooldown = byCommand ? 0 : 500; // do a short pause if interupted by event
        }


        private void CalculatePath()
        {
            Location currentLocation = _creature.Location;
            Location targetLocation = new Location(_command.X, _command.Y);
            _path = new List<Location>();
            Location iterativeLocation = currentLocation;
            do
            {
                if (iterativeLocation.X > targetLocation.X)
                    iterativeLocation.X -= 1;
                if (iterativeLocation.X < targetLocation.X)
                    iterativeLocation.X += 1;

                if (iterativeLocation.Y > targetLocation.Y)
                    iterativeLocation.Y -= 1;
                if (iterativeLocation.Y < targetLocation.Y)
                    iterativeLocation.Y += 1;

                _path.Add(new Location(iterativeLocation.X, iterativeLocation.Y));

            } while (iterativeLocation != targetLocation);
        }

    }
}
